package com.xuecheng.auth.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.client.XcServiceList;
import com.xuecheng.framework.domain.ucenter.ext.AuthToken;
import com.xuecheng.framework.domain.ucenter.request.LoginRequest;
import com.xuecheng.framework.domain.ucenter.response.AuthCode;
import com.xuecheng.framework.exception.ExceptionCast;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author ShengJinFeng
 * @version V1.0
 * @ClassName AuthService
 * @Description (用户认证)
 * @date 2020/3/10 20:28
 * @since V1.0
 */
@Service
public class AuthService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthService.class);

    @Value("${auth.clientId}")
    private String clientId;

    @Value("${auth.clientSecret}")
    private String clientSecret;

    @Value("${auth.tokenValiditySeconds}")
    private int tokenValiditySeconds;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * @Description 登录认证获取令牌
     * @author ShengJinFeng
     * @date 2020/3/10
     */
    public AuthToken loginAuth(LoginRequest loginRequest) {
        //校验账号是否输入
        if(loginRequest == null || StringUtils.isEmpty(loginRequest.getUsername())){
            ExceptionCast.cast(AuthCode.AUTH_USERNAME_NONE); }
        //校验密码是否输入
        if(StringUtils.isEmpty(loginRequest.getPassword())){
            ExceptionCast.cast(AuthCode.AUTH_PASSWORD_NONE);
        }
        String username = loginRequest.getUsername();
        String password = loginRequest.getPassword();
        //申请令牌
        AuthToken authToken = applyToken(username, password, clientId, clientSecret);
        //将令牌记录到redis
        String accessToken = authToken.getAccess_token();
        String content = JSON.toJSONString(authToken);
        boolean saveResult = saveRedis(accessToken,content,tokenValiditySeconds);
        if (!saveResult){
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_TOKEN_SAVEFAIL);
        }
        return authToken;
    }

    /**
     * @Description 根据访问token查询userjwt令牌
     * @author ShengJinFeng
     * @date 2020/3/11
     */
    public AuthToken findJwtByToken(String accessToken) {
        String key = "user_token:"+accessToken;
        String token = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isEmpty(token)){
            return null;
        }
        AuthToken authToken = JSON.parseObject(token, AuthToken.class);
        return authToken;
    }

    /**
     * @Description 从redis中删除令牌
     * @author ShengJinFeng
     * @date 2020/3/12
     */
    public boolean deleteRedis(String accessToken) {
        //令牌key
        String key = "user_token:"+accessToken;
        return stringRedisTemplate.delete(key);
    }

    /**
     * @Description 将令牌记录到redis
     * @author ShengJinFeng
     * @date 2020/3/11
     */
    private boolean saveRedis(String access_token,String content,long ttl){
        //令牌key
        String key = "user_token:"+access_token;
        stringRedisTemplate.boundValueOps(key).set(content,tokenValiditySeconds, TimeUnit.SECONDS);
        //获取过期时间，如果小于0表示已过期
        Long expire = stringRedisTemplate.getExpire(key);
        return expire>0;
    }

    /**
     * @Description 申请令牌
     * @author ShengJinFeng
     * @date 2020/3/10
     */
    private AuthToken applyToken(String username,String password,String clientId,String clientSecret){
        //通过负载均衡获取服务地址
        ServiceInstance instance = loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
        //得到请求地址
        String authUrl = instance.getUri()+"/auth/oauth/token";
        //设置请求内容header和body
        MultiValueMap<String,String> headers = new LinkedMultiValueMap<String, String>();
        String httpbasic  = httpBasic(clientId,clientSecret);
        headers.add("Authorization",httpbasic);
        //设置请求体,包括：grant_type、username、passowrd
        MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
        body.add("grant_type","password");
        body.add("username",username);
        body.add("password",password);
        HttpEntity<MultiValueMap<String, String>> multiValueMapHttpEntity = new HttpEntity<MultiValueMap<String, String>>(body, headers);
        //指定 restTemplate当遇到400或401响应时候也不要抛出异常，也要正常返回值
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                //设置不等于400和401的时候才抛异常
                if (response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
                    super.handleError(response);
                }
            }
        });
        //申请令牌
        ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, multiValueMapHttpEntity, Map.class);
        Map result = exchange.getBody();
        if (result==null || result.get("value")==null){
            if(result.get("message") != null && StringUtils.equals((String) result.get("message"),"坏的凭证")){
                ExceptionCast.cast(AuthCode.AUTH_CREDENTIAL_ERROR);
            }else if (StringUtils.contains((String) result.get("message"),"UserDetailsService returned null")){
                ExceptionCast.cast(AuthCode.AUTH_ACCOUNT_NOTEXISTS);
            }
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        AuthToken authToken = new AuthToken();
        Object additionalInformation = result.get("additionalInformation");
        if (additionalInformation==null){
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        Map jtiMap = JSON.parseObject(JSON.toJSONString(additionalInformation), Map.class);
        if (jtiMap.get("jti") == null){
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        String accessToken = (String) jtiMap.get("jti");
        authToken.setAccess_token(accessToken);
        Object refresh = result.get("refreshToken");
        if (refresh==null){
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        Map refreshMap = JSON.parseObject(JSON.toJSONString(refresh), Map.class);
        if (refreshMap.get("value") == null){
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        String refreshToken = (String) refreshMap.get("value");
        authToken.setRefresh_token(refreshToken);
        String jwtToken = (String) result.get("value");
        authToken.setJwt_token(jwtToken);
        return authToken;
    }

    /**
     * @Description 获取basic认证码
     * @author ShengJinFeng
     * @date 2020/3/10
     */
    private String httpBasic(String clientId, String clientSecret) {
        //将客户端id和客户端密码拼接，按“客户端id:客户端密码”
        String string = clientId+":"+clientSecret;
        //进行base64编码
        byte[] encode = Base64Utils.encode(string.getBytes());
        return "Basic "+new String(encode);
    }
}
